/***********************************************************************************************************************
*                                                                                                                      *
* SPLASH build system v0.1                                                                                             *
*                                                                                                                      *
* Copyright (c) 2013 Andrew D. Zonenberg                                                                               *
* All rights reserved.                                                                                                 *
*                                                                                                                      *
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the     *
* following conditions are met:                                                                                        *
*                                                                                                                      *
*    * Redistributions of source code must retain the above copyright notice, this list of conditions, and the         *
*      following disclaimer.                                                                                           *
*                                                                                                                      *
*    * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the       *
*      following disclaimer in the documentation and/or other materials provided with the distribution.                *
*                                                                                                                      *
*    * Neither the name of the author nor the names of any contributors may be used to endorse or promote products     *
*      derived from this software without specific prior written permission.                                           *
*                                                                                                                      *
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED   *
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL *
* THE AUTHORS BE HELD LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES        *
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR       *
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT *
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE       *
* POSSIBILITY OF SUCH DAMAGE.                                                                                          *
*                                                                                                                      *
***********************************************************************************************************************/

#include "splashcore.h"

using namespace std;

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Construction / destruction

FPGABitstreamNode::FPGABitstreamNode(
	BuildGraph* graph,
	vector<string> sources,
	BuildGraphNode* netlist,
	FPGABitstreamGenerationFlagList flags,
	FPGAToolchain* toolchain,
	string device)
	: BuildGraphNode(graph)
	, m_netlist(netlist)
	, m_flags(flags)
	, m_toolchain(toolchain)
	, m_device(device)
{
	m_sources = sources;
	
	//TODO: Make this portable
	RoutedNCDNetlistNode* node = dynamic_cast<RoutedNCDNetlistNode*>(m_netlist);
	if(node == NULL)
		FatalError("FPGABitstreamNode expects a RoutedNCDNetlistNode\n");
	
	m_name = node->GetName();
}

FPGABitstreamNode::~FPGABitstreamNode()
{
	
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Factory method

FPGABitstreamNode* FPGABitstreamNode::CreateFPGABitstreamNode(
		BuildGraph* graph,
		vector<string> sources,
		BuildGraphNode* netlist,
		FPGABitstreamGenerationFlagList flags,
		FPGAToolchain* toolchain,
		string device,
		bool target)
{
	//Create the netlist node
	FPGABitstreamNode* node = new FPGABitstreamNode(graph, sources, netlist, flags, toolchain, device);
	graph->AddNodeWithPath(node, node->GetOutputPath(), device);
	
	//Add it as a top-level target if requested
	if(target)
		graph->AddTarget(node, node->m_name);
	
	//Done
	return node;
}

FPGABitstreamNode* FPGABitstreamNode::CreateFPGABitstreamNode(
		BuildGraph* graph, 
		vector<string> sources,
		string name,
		string top_level,
		string device,
		string constraints,
		FPGASynthesisFlagList xstflags,
		XilinxFPGAMapFlagList mapflags,
		FPGAPlaceAndRouteFlagList parflags,
		FPGATimingAnalysisFlagList timeflags,
		FPGABitstreamGenerationFlagList bitgenflags,
		XilinxFPGAToolchain* toolchain,
		bool target
		)
{
	NGCNetlistNode* ngc = NGCNetlistNode::CreateNGCNetlistNode(
		graph, sources, toolchain, name, top_level, device, xstflags);
	NGDNetlistNode* ngd = NGDNetlistNode::CreateNGDNetlistNode(
		graph, ngc, constraints, device, toolchain);
	UnroutedNCDNetlistNode* unrouted = UnroutedNCDNetlistNode::CreateUnroutedNCDNetlistNode(
		graph, ngd, device, mapflags, toolchain);
	RoutedNCDNetlistNode* routed = RoutedNCDNetlistNode::CreateRoutedNCDNetlistNode(
		graph, unrouted, parflags, toolchain);
	
	//TODO: Support stuff other than Xilinx here
	//Xilinx's format is XCfoo-N-pkg with speed grade N.
	const char* dashpos = strstr(device.c_str(), "-");
	int speedgrade = 0;
	if(dashpos != 0)
		sscanf(dashpos+1, "%d", &speedgrade);
			
	FPGAStaticTimingAnalysisNode::CreateFPGAStaticTimingAnalysisNode(graph, routed, speedgrade, timeflags, toolchain, true);
	return FPGABitstreamNode::CreateFPGABitstreamNode(graph, sources, routed, bitgenflags, toolchain, device,target);
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Accessors

string FPGABitstreamNode::GetOutputPath()
{
	return m_graph->GetOutDir() + "/" + m_toolchain->GetArchitecture(m_device) + "/" + m_name + ".bit";
}

string FPGABitstreamNode::GetDescription()
{
	char desc[1024];
	snprintf(desc, sizeof(desc), "FPGA bitstream %s", m_name.c_str());
	return string(desc);
}

string FPGABitstreamNode::GetBinaryPath()
{
	string hash = GetHashCached();
	string outdir = m_graph->GetMetaDir() + "/" + hash;
	return outdir + "/" + m_name + ".bit";
}

bool FPGABitstreamNode::GetFilteredOutput(vector<string>& output)
{
	static const char* whitelist[]=
	{
		"ERROR",
		"error",
		"WARNING",
		"warning"
	};
	
	static const char* blacklist[]=
	{
		"WARNING:PhysDesignRules:367",		//Loadless signal (if present, will be warned by PAR too)
		"WARNING:PhysDesignRules:2410"		//Design is using a 9kbit block RAM
	};
	
	return BuildGraphNode::GetFilteredOutput(
		output,
		whitelist,
		sizeof(whitelist)/sizeof(whitelist[0]),
		blacklist,
		sizeof(blacklist)/sizeof(blacklist[0])
		);
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Building

void FPGABitstreamNode::UpdateDependencies()
{
	DebugPrintfHeading(4, "FPGABitstreamNode::UpdateDependencies()\n");
	
	m_dependencies.clear();
	m_dependencies.push_back(m_netlist);
}

string FPGABitstreamNode::GetHash()
{	
	DebugPrintfHeading(4, "FPGABitstreamNode::GetHash() [node = \"%s\"]\n", m_name.c_str());
	string hashin = "";
	
	//Update our dependencies
	UpdateDependencies();

	//Add data at this node
	hashin += sha256(m_name);
	hashin += m_toolchain->GetHash();
	hashin += sha256(m_device);
	for(size_t i=0; i<m_flags.size(); i++)
		hashin += sha256(m_flags[i]->toString(m_toolchain));
		
	//Add data for our dependencies
	for(size_t i=0; i<m_dependencies.size(); i++)
		hashin += m_dependencies[i]->GetHashCached(); 
	
	return sha256(hashin);
}

void FPGABitstreamNode::Update(Cluster* cluster)
{
	//Rebuild all of our dependencies if necessary
	vector<ClusterJob*> deps;
	for(size_t i=0; i<m_dependencies.size(); i++)
	{		
		BuildGraphNode* node = m_dependencies[i];
		node->UpdateCached(cluster);
		if(node->GetBuildJob() != NULL)
			deps.push_back(node->GetBuildJob());
	}
	
	//TODO: Make this portable
	RoutedNCDNetlistNode* node = dynamic_cast<RoutedNCDNetlistNode*>(m_netlist);
	if(node == NULL)
		FatalError("FPGABitstreamNode::Update() expects a RoutedNCDNetlistNode\n");
	
	//Generate the PCF filename
	string netlist = m_netlist->GetBinaryPath();
	string unrouted_netlist = node->GetUnroutedNetlist()->GetBinaryPath();
	string dir = GetDirOfFile(unrouted_netlist);
	string base = GetBasenameOfFileWithoutExt(unrouted_netlist);
	string pcf = dir + "/" + base + ".pcf";

	//Run the actual bitstream-generation step
	m_job = m_toolchain->GenerateBitstream(
		m_netlist->GetBinaryPath(),
		pcf,
		GetBinaryPath(),
		m_device,
		deps,
		m_flags,
		cluster);
}
